#include "libspngstream.h"

png_structp png_ptr;
png_infop info_ptr;

unsigned int width;
unsigned int height;
int bit_depth;
int color_type;
double LUT_exponent;
double CRT_exponent;
double default_display_exponent;

void printpng_version() {
    printf("libspngstream 2nd edition v2.31\n");
    /* print stats about loaded image */
    readpng_version_info();
}

void printpng_meta(spng* png) {
    /* printf args are split line wise to reflect their position */
    /* within the multi line string */
    printf("file %s\n\
file width/height: %ldx%ld\n\
file aspect ratio: %ld:%ld\n\
file background: %i, %i, %i\n\
file image data:\n\
  bit_depth %d\n\
  color_type %d\n\
  rowbytes %li\n\
  channels %d\n",
        png->png_filename,
        png->width, png->height,
        png->aspect_ratio_w, png->aspect_ratio_h,
        png->bg_r, png->bg_g, png->bg_b,
        png->bit_depth,
        png->color_type,
        png->rowbytes,
        png->channels
    );
}

void printpng_content(spng* png) {
    png_byte r, g, b, a = 0;
    for (int y = 0; y < png->height; y++) {
        png_byte* row = png->row_pointers[y];
        for (int x = 0; x < png->width; x++) {
            png_byte *ptr = &(row[x * png->channels]);
            r = ptr[0];
            g = ptr[1];
            b = ptr[2];
            a = png->channels == 4 ? ptr[3] : 255;
            printf("  pos(%4d, %4d) RGBA(%3d, %3d, %3d, %3d)\n", x, y, r, g, b, a);
        }
    }
}

spng *readpng(char *file) {
    /* initialize spng structure to store info */
    spng *png = (spng *)malloc(sizeof(spng));
    png->bg_r = 0;
    png->bg_g = 0;
    png->bg_b = 0;

    /* open target file for reading */
    png->png_filename = file;
    png->png_file = fopen(file, "r");
    if (!png->png_file) {
        fprintf(stderr, "libspngstream: error opening file '%s' for reading\n", file);
        exit(ERR_IFILE);
    }

    /* initialize libpng */
    int ret = readpng_init(png->png_file, &(png->width), &(png->height), &(png->bit_depth), &(png->color_type));
    if (ret) {
        fprintf(stderr, "libspngstream: error while initializing readpng (error code %d)\n", ret);
        readpng_cleanup();
        exit(ret);
    }

    /* calculate metadata */
    png->aspect_ratio_w = png->width;
    png->aspect_ratio_h = png->height;
    simplifyfractionl(&(png->aspect_ratio_w), &(png->aspect_ratio_h));

    /* grab background information */
    readpng_get_bgcolor(&(png->bg_r), &(png->bg_g), &(png->bg_b));

    /* grab actual image */
    readpng_get_image(&(png->channels), &(png->rowbytes), &(png->image_data), &(png->row_pointers));

    /* check for success */
    if (png->image_data == NULL) {
        fprintf(stderr, "libspngstream: readpng_get_image could not allocate memory\n");
        exit(ERR_MEMALLOC);
    }

    /* finalize structure */
    return png;
}

spng *copypng(spng *src) {
    /* initialize spng structure to store info */
    spng *png = (spng *)malloc(sizeof(spng));

    /* copy members */
    png->width = src->width;
    png->height = src->height;
    png->aspect_ratio_w = src->aspect_ratio_w;
    png->aspect_ratio_h = src->aspect_ratio_h;

    png->color_type = src->color_type;
    png->bit_depth = src->bit_depth;
    png->channels = src->channels;
    png->rowbytes = src->rowbytes;

    /* reallocate the entire image and create a deep copy */
    png->image_data = (unsigned char*)malloc(src->rowbytes * src->height);
    memcpy(png->image_data, src->image_data, src->rowbytes * src->height);

    /* reassign new row_pointers */
    png->row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * src->height);
    for (int row = 0; row < src->height; row++)
        png->row_pointers[row] = png->image_data + (row * src->rowbytes);

    /* copy members */
    png->bg_r = src->bg_r;
    png->bg_g = src->bg_g;
    png->bg_b = src->bg_b;

    /* dummy commponents, no copy necessary */
    png->png_filename = "//copy";
    png->png_file = NULL;

    return png;
}

void writepng(spng *png, char *file) {
    /* open target file for reading */
    FILE *out = fopen(file, "wb");
    if (!out) {
        fprintf(stderr, "libspngstream: error opening file '%s' for writing\n", file);
        exit(ERR_OFILE);
    }

    int ret = writepng_all(out, png);
    writepng_cleanup();
    if (ret) {
        fprintf(stderr, "libspngstream: error while writing output png (error code %d)\n", ret);
        exit(ret);
    }

    fclose(out);
}

void closepng(spng *png) {
    if (png->png_file)
        fclose(png->png_file);
    readpng_cleanup();
}

void freepng(spng *png) {
    free(png->image_data);
    free(png->row_pointers);
    free(png);
}
